Control Templates
Every control in
Silverlight has XAML that defines how a control is drawn. For example,
the humble button uses this XAML to draw itself:
<Grid Background="Transparent">
...
<Border x:Name="ButtonBackground"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
CornerRadius="0"
Margin="{StaticResource PhoneTouchTargetOverhang}">
<ContentControl x:Name="ContentContainer"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
Foreground="{TemplateBinding Foreground}"
HorizontalAlignment="{TemplateBinding
HorizontalContentAlignment}"
Padding="{TemplateBinding Padding}"
VerticalAlignment="{TemplateBinding
VerticalContentAlignment}" />
</Border>
</Grid>
Even though you think of
controls as atomic elements, there is XAML inside the control to define
the look and feel of the control. Control templates are used to
re-define this XAML for any control.
Control templates are part of the style that is applied to a control. The ControlTemplate is the value of the Template property of any control. For example:
<Style x:Key="ButtonStyle1"
TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid Background="Transparent">
...
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The contents of the ControlTemplate
contains the XAML that the particular control should use (i.e. Button
in this case). As you define the XAML that makes up the look of a
particular control, you can use a markup extension called
TemplateBinding to pull the value of a property into your XAML. For
example:
<ControlTemplate TargetType="Button">
<Grid Background="Transparent">
<Border x:Name="ButtonBackground"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}">
...
</Border>
</Grid>
</ControlTemplate>
By using template bindings,
the natural value of the property will be used as the value inside the
control template. This value could come from the default value in the
control, a style setter or specifically set on an instance of the
control. Template bindings allow you to use whatever property value is
supposed to be show in the control.
For some controls, the XAML must have certain elements to make sure the control still works. For example the WebBrowser control requires that there be a part of the XAML that is a Border element named PresentationContainer. That way the control knows where to show the web content. This contract between you and the control author is called a TemplatePart
(many controls do not have any template parts.). The template parts
that are required are documented as attributes on the controls
themselves (as seen in Figure 1).
The structure of
the XAML in a control template represents the look of the control, but
in addition you can specify the feel of an application as well. The
feel of an application is the way that the control can interact with
the users. For example, the Button class changes its appearance when
pressed to give that feedback to the user that they’ve correctly
pressed the button. This is the way that the feel of an application
works.
The feel of an application
can be created using a structure called the Visual State Manager. The
visual state manager is a way of defining animations that represent a
state that the control can be in. The visual state manager is used by
the control to go into specific states as the user interacts with it.
As the application designer you can define these states to change the
way that the control interacts with the user. These states are broken
up in to groups so a single control can be in more than one state. For
example, two of the visual state manager groups that the TextBox has are CommonStates (which represent states like Disabled and ReadOnly) and FocusStates
(which represent whether the control has focus or not). You could
imagine that a control could be in both an unfocused state and be
disabled.
To define the groups and
states for the visual state manager, the XAML can contain a
VisualStateManager.VisualStateGroups property (as an attached property):
<ControlTemplate TargetType="Button">
<Grid Background="Transparent">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver" />
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetProperty="Foreground"
Storyboard.TargetName="ContentContainer">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{StaticResource
PhoneBackgroundBrush}" />
</ObjectAnimationUsingKeyFrames>
...
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetProperty="Foreground"
Storyboard.TargetName="ContentContainer">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{StaticResource
PhoneDisabledBrush}" />
</ObjectAnimationUsingKeyFrames>
...
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
...
</Grid>
</ControlTemplate>
As this example shows, the VisualStateManager.VisualStateGroups attached property contains one (or more) VisualStateGroup objects. Inside the group is a list of VisualState objects that represent a Storyboard that shows how to go to a specific state. The empty VisualState objects mean that that state should look exactly like the natural state of the object.
The visual states and groups that a control supports are also specified as attributes on the control classes as seen in Figure 2.
When creating your own
control templates you will need to be aware of the template parts and
template visual states as that is the contract between you and the
control author. You must implement these states and parts if you expect
the controls to continue to operate correctly.